﻿module    WingBeats.Xml.Render

open System
open System.IO
open System.Xml
open WingBeats.Xml
open WingBeats.Xml.Xss

let rec renderTemplate lookup (writer:XmlWriter) node =
    let renderAttr (k,v) =
        writer.WriteAttributeString(k, v)
    let renderOpening tag attrs =
        writer.WriteStartElement(tag)
        attrs |> List.iter renderAttr
    let renderSelfClosing tag attrs =
        renderOpening tag attrs
        writer.WriteEndElement()
    let renderTagPair tag attrs children =
        renderOpening tag attrs
        if Seq.isEmpty(children) then
            writer.WriteString(String.Empty) // force it to avoid a self-closing <tag />
        else
            children |> Seq.iter (renderTemplate lookup writer)
        writer.WriteEndElement()
    let renderPlaceholder id =
        // This can throw a KeyNotFoundException, warning the page author
        // that their code doesn't match their template.
        let f = Map.find id lookup
        f() |> renderTemplate lookup writer

    match node with
    | Text(s)                           -> writer.WriteString(s)
    | LiteralText(s)                    -> writer.WriteRaw(s)
    | SelfClosingNode(tag, attrs)       -> renderSelfClosing tag attrs
    | TagPairNode(tag, attrs, children) -> renderTagPair tag attrs children
    | Placeholder(id)                   -> renderPlaceholder id
    | NoNode                            -> ()

let renderNode : XmlWriter -> Node -> unit = renderTemplate Map.empty

let private settings = XmlWriterSettings(OmitXmlDeclaration=false, ConformanceLevel=ConformanceLevel.Fragment)

let renderNodeText (tw:TextWriter) node =
    use writer = XmlWriter.Create(tw, settings)
    renderNode writer node

let renderTemplateToString lookup node =
    use sw = new StringWriter()
    use writer = XmlWriter.Create(sw, settings)
    renderTemplate lookup writer node
    writer.Flush()
    sw.ToString()

let renderToString = renderTemplateToString Map.empty

let renderManyTemplates lookup writer (nodes:#seq<Node>) =
    nodes |> Seq.iter (renderTemplate lookup writer)

let renderManyTemplatesToString lookup (nodes:#seq<Node>) =
    use sw = new StringWriter()
    use writer = XmlWriter.Create(sw, settings)
    renderManyTemplates lookup writer nodes
    writer.Flush()
    sw.ToString()

let renderManyNodes writer (nodes:#seq<Node>) =
    nodes |> Seq.iter (renderNode writer)

let renderManyToString (nodes:#seq<Node>) =
    use sw = new StringWriter()
    use writer = XmlWriter.Create(sw, settings)
    renderManyNodes writer nodes
    writer.Flush()
    sw.ToString()